// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.

#include "test_precomp.hpp"

#include <bitset>

namespace opencv_test { namespace {
using namespace cv::img_hash;

/**
 *The expected results of this test case are come from the Phash library,
 *I use it as golden model
 */
class CV_BlockMeanHashTest : public cvtest::BaseTest
{
public:
    CV_BlockMeanHashTest();
protected:
    void run(int /* idx */);

    void testMeanMode0();
    void testMeanMode1();
    void testHashMode0();
    void testHashMode1();

    cv::Mat input;
    cv::Mat hash;
    Ptr<cv::img_hash::BlockMeanHash> bmh;
};

CV_BlockMeanHashTest::CV_BlockMeanHashTest()
{
    input.create(256, 256, CV_8U);
    for(int row = 0; row != input.rows; ++row)
    {
        uchar value = static_cast<uchar>(row);
        for(int col = 0; col != input.cols; ++col)
        {
            input.at<uchar>(row, col) = value++;
        }
    }
    bmh = BlockMeanHash::create(BLOCK_MEAN_HASH_MODE_0);
}

void CV_BlockMeanHashTest::testMeanMode0()
{
    std::vector<double> const &features = bmh->getMean();
    double const expectResult[] =
    {15,31,47,63,79,95,111,127,143,159,175,191,207,223,239,135,
     31,47,63,79,95,111,127,143,159,175,191,207,223,239,135,15,
     47,63,79,95,111,127,143,159,175,191,207,223,239,135,15,31,
     63,79,95,111,127,143,159,175,191,207,223,239,135,15,31,47,
     79,95,111,127,143,159,175,191,207,223,239,135,15,31,47,63,
     95,111,127,143,159,175,191,207,223,239,135,15,31,47,63,79,
     111,127,143,159,175,191,207,223,239,135,15,31,47,63,79,95,
     127,143,159,175,191,207,223,239,135,15,31,47,63,79,95,111,
     143,159,175,191,207,223,239,135,15,31,47,63,79,95,111,127,
     159,175,191,207,223,239,135,15,31,47,63,79,95,111,127,143,
     175,191,207,223,239,135,15,31,47,63,79,95,111,127,143,159,
     191,207,223,239,135,15,31,47,63,79,95,111,127,143,159,175,
     207,223,239,135,15,31,47,63,79,95,111,127,143,159,175,191,
     223,239,135,15,31,47,63,79,95,111,127,143,159,175,191,207,
     239,135,15,31,47,63,79,95,111,127,143,159,175,191,207,223,
     135,15,31,47,63,79,95,111,127,143,159,175,191,207,223,239,};
    for(size_t i = 0; i != features.size(); ++i)
    {
        ASSERT_NEAR(features[i], expectResult[i], 0.0001);
    }
}

void CV_BlockMeanHashTest::testMeanMode1()
{
    std::vector<double> const &features = bmh->getMean();
    double const expectResult[] =
    {15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,
     23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,
     31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,
     39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,
     47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,
     55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,
     63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,
     71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,
     79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,
     87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,
     95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,
     103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,
     111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,
     119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,
     127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,
     135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,
     143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,
     151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,
     159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,
     167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,
     175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,
     183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,
     191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,
     199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,
     207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,
     215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,
     223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,
     231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,
     239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,
     219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,
     135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,};
    for(size_t i = 0; i != features.size(); ++i)
    {
        ASSERT_NEAR(features[i], expectResult[i], 0.0001);
    }
}

void CV_BlockMeanHashTest::testHashMode0()
{
    bool const expectResult[] =
    {0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
     0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,
     0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,
     0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,
     0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
     0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,
     0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
     0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,
     1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
     1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
     1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,
     1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,
     1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,
     1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,
     1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
     1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,
    };

    for(int i = 0; i != hash.cols; ++i)
    {
        std::bitset<8> const bits = hash.at<uchar>(0, i);
        for(size_t j = 0; j != bits.size(); ++j)
        {
            EXPECT_EQ(expectResult[i*8+j], bits[j]);
        }
    }
}

void CV_BlockMeanHashTest::testHashMode1()
{
    bool const expectResult[] =
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
     1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
     1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,
     1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
     1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,
     1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
     1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,
     1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
     1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,
     1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
     1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,
     1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
     1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    };

    for(int i = 0; i != hash.cols; ++i)
    {
        std::bitset<8> const bits = hash.at<uchar>(0, i);
        if(i != hash.cols-1)
        {
            for(size_t j = 0; j != bits.size(); ++j)
            {
                EXPECT_EQ(expectResult[i*8+j], bits[j]);
            }
        }
        else
        {
            //when mode == 1, there will be 961 block mean
            //that is why we only check one bit at here
            EXPECT_EQ(expectResult[i*8], bits[0]);
        }
    }
}

void CV_BlockMeanHashTest::run(int)
{
    bmh->compute(input, hash);
    testMeanMode0();
    testHashMode0();

    bmh->setMode(BLOCK_MEAN_HASH_MODE_1);
    bmh->compute(input, hash);
    testMeanMode1();
    testHashMode1();
}

TEST(block_mean_hash_test, accuracy) { CV_BlockMeanHashTest test; test.safe_run(); }

}} // namespace
